package ch.ethz.inf.shop.model;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.math.BigDecimal;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;

import ch.ethz.inf.shop.model.simpleDatabase.Tuple;
import ch.ethz.inf.shop.model.simpleDatabase.TupleSchema;
import ch.ethz.inf.shop.model.simpleDatabase.operators.GroupBy;
import ch.ethz.inf.shop.model.simpleDatabase.operators.GroupBy.AggregateFunction;
import ch.ethz.inf.shop.model.simpleDatabase.operators.Insert;
import ch.ethz.inf.shop.model.simpleDatabase.operators.Join;
import ch.ethz.inf.shop.model.simpleDatabase.operators.LeftJoin;
import ch.ethz.inf.shop.model.simpleDatabase.operators.Operator;
import ch.ethz.inf.shop.model.simpleDatabase.operators.Project;
import ch.ethz.inf.shop.model.simpleDatabase.operators.Scan;
import ch.ethz.inf.shop.model.simpleDatabase.operators.Select;
import ch.ethz.inf.shop.model.simpleDatabase.operators.Sort;
import ch.ethz.inf.shop.model.simpleDatabase.operators.TupleAccepter;
import ch.ethz.inf.shop.model.simpleDatabase.operators.TupleProjector;

public final class DatastoreInterfaceSimpleDatabase implements DatastoreInterface {
	private static final Logger LOGGER = Logger.getLogger(DatastoreInterfaceSimpleDatabase.class);
	public static final String ROOT_DIRECTORY = "/home/rop/Dropbox/ETH/Studium/Fächer/Datenbanken/projekt_1/workspace/commodity-shop/db/project2data";

	private static Map<String, Comparator<Tuple>> RECIPE_COMPARATORS = new HashMap<String, Comparator<Tuple>>();

	private final String rootDirectory;
	static {
		RECIPE_COMPARATORS.put("price", new Comparator<Tuple>() {
			@Override
			public int compare(Tuple o1, Tuple o2) {
				return -o1.getBigDecimal("recipe.cost_of_ingredients").compareTo(o2.getBigDecimal("recipe.cost_of_ingredients"));
			}
		});
		RECIPE_COMPARATORS.put("name", new Comparator<Tuple>() {
			@Override
			public int compare(Tuple o1, Tuple o2) {
				return o1.getString("recipe.name").toLowerCase().compareTo(o2.getString("recipe.name").toLowerCase());
			}
		});
	}

	public DatastoreInterfaceSimpleDatabase(String rootDirectory) {
		this.rootDirectory = rootDirectory;
	}

	public DatastoreInterfaceSimpleDatabase() {
		this.rootDirectory = ROOT_DIRECTORY;
	}

	@Override
	public final Product getProductById(final int id) {
		Operator op = getAllProductData();

		op = new Select(op, new TupleAccepter() {
			@Override
			public boolean acceptTuple(Tuple t) {
				return t.getInt("product.product_id") == id;
			}
		});

		return op.moveNext() ? new Product(op.current()) : null;

	}

	@Override
	public final List<Product> getAllProducts() {

		Operator op = getAllProductData();

		List<Product> retList = new ArrayList<Product>();
		while (op.moveNext()) {
			retList.add(new Product(op.current()));
		}
		return retList;
	}

	@Override
	public final List<Product> getProductsByName(final String name) {

		Operator op = getAllProductData();

		op = new Select(op, new TupleAccepter() {
			@Override
			public boolean acceptTuple(Tuple t) {
				return t.getString("product.name").toLowerCase().contains(name.toLowerCase());
			}
		});

		List<Product> retList = new ArrayList<Product>();
		while (op.moveNext()) {
			retList.add(new Product(op.current()));
		}
		return retList;

	}

	private LeftJoin getAllProductData() {
		Operator leftOp = new Scan(rootDirectory + "/product.txt", "product");
		Operator rightOp = new Scan(rootDirectory + "/brand.txt", "brand");
		leftOp = new Join(leftOp, "product.brand", rightOp, "brand.brand_id");

		rightOp = new Scan(rootDirectory + "/category.txt", "category");
		leftOp = new Join(leftOp, "product.category", rightOp, "category.category_id");

		rightOp = new Scan(rootDirectory + "/like.txt", "like");
		rightOp = new GroupBy(rightOp, AggregateFunction.COUNT, "like.user_id", "like_cnt", "like.product_id");
		return new LeftJoin(leftOp, "product.product_id", rightOp, "like.product_id");
	}

	@Override
	public final List<Product> getProductsByCategory(final Category category) {

		Operator op = getAllProductData();

		op = new Select(op, new TupleAccepter() {
			@Override
			public boolean acceptTuple(Tuple t) {
				return t.getString("category.name").toLowerCase().contains(category.getName().toLowerCase());
			}
		});

		List<Product> retList = new ArrayList<Product>();
		while (op.moveNext()) {
			retList.add(new Product(op.current()));
		}
		return retList;
	}

	@Override
	public final List<Product> getProductsByBrand(final Brand itemBrand) {

		Operator op = getAllProductData();

		op = new Select(op, new TupleAccepter() {
			@Override
			public boolean acceptTuple(Tuple t) {
				return t.getString("brand.name").toLowerCase().contains(itemBrand.getName().toLowerCase());
			}
		});

		List<Product> retList = new ArrayList<Product>();
		while (op.moveNext()) {
			retList.add(new Product(op.current()));
		}
		return retList;
	}

	@Override
	public final List<Category> getAllCategories() {

		final Scan category = new Scan(rootDirectory + "/category.txt", "category");

		List<Category> retList = new ArrayList<Category>();
		while (category.moveNext()) {
			retList.add(new Category(category.current()));
		}
		return retList;
	}

	@Override
	public final List<Brand> getAllBrands() {

		final Scan brand = new Scan(rootDirectory + "/brand.txt", "brand");
		List<Brand> retList = new ArrayList<Brand>();
		while (brand.moveNext()) {
			retList.add(new Brand(brand.current()));
		}
		return retList;

	}

	@Override
	public final List<Product> getProductsByPopularity() {

		Operator op = getAllProductData();

		op = new Sort(op, new Comparator<Tuple>() {
			@Override
			public int compare(Tuple o1, Tuple o2) {
				return -((Integer) o1.getInt("like_cnt")).compareTo(o2.getInt("like_cnt"));
			}
		});

		List<Product> retList = new ArrayList<Product>();
		while (op.moveNext()) {
			retList.add(new Product(op.current()));
		}
		return retList;

	}

	@Override
	public List<Product> getProductsByBrandName(final String brandName) {

		Operator op = getAllProductData();
		op = new Select(op, new TupleAccepter() {
			@Override
			public boolean acceptTuple(Tuple t) {
				return t.getString("brand.name").toLowerCase().contains(brandName.toLowerCase());
			}
		});

		List<Product> retList = new ArrayList<Product>();
		while (op.moveNext()) {
			retList.add(new Product(op.current()));
		}
		return retList;
	}

	@Override
	public List<Product> getProductsByCategoryName(final String categoryName) {

		Operator op = getAllProductData();

		op = new Select(op, new TupleAccepter() {
			@Override
			public boolean acceptTuple(Tuple t) {
				return t.getString("category.name").toLowerCase().contains(categoryName.toLowerCase());
			}
		});

		List<Product> retList = new ArrayList<Product>();
		while (op.moveNext()) {
			retList.add(new Product(op.current()));
		}
		return retList;
	}

	@Override
	public List<Comment> getCommentsByProduct(final Product product) {
		Operator op = new Scan(rootDirectory + "/comment.txt", "comment");

		op = new Select(op, new TupleAccepter() {

			@Override
			public boolean acceptTuple(Tuple t) {
				return t.getInt("comment.product") == product.getProductId();
			}
		});
		List<Comment> retVal = new ArrayList<Comment>();

		final Scan userScan = new Scan(rootDirectory + "/registered_user.txt", "registered_user");

		op = new Join(userScan, "registered_user.user_id", op, "comment.user");

		while (op.moveNext()) {
			try {
				Comment c = new Comment(op.current());
				c.setProduct(product);
				retVal.add(c);
			} catch (ParseException e) {
				LOGGER.error("Could not create comment", e);
			}
		}
		return retVal;
	}

	@Override
	public RegisteredUser getUserDetailsFor(final String username, final String password) {
		Operator op = new Scan(rootDirectory + "/registered_user.txt", "registered_user");
		op = new Select(op, new TupleAccepter() {

			@Override
			public boolean acceptTuple(Tuple t) {
				return t.getString("registered_user.name").equals(username) && t.getString("registered_user.passwd").equals(password);
			}
		});

		return op.moveNext() ? new RegisteredUser(op.current()) : null;
	}

	@Override
	public List<Recipe> getRecipes(final String orderBy) {
		Operator j = getAllRecipes();

		if (orderBy != null && !"".equals(orderBy)) {
			j = new Sort(j, RECIPE_COMPARATORS.get(orderBy));
		}
		List<Recipe> retVal = new ArrayList<Recipe>();
		while (j.moveNext()) {
			try {
				retVal.add(new Recipe(j.current()));
			} catch (ParseException e) {
				LOGGER.error("could not create recipe", e);
			}
		}
		return retVal;
	}

	private Join getAllRecipes() {
		Operator leftOp = new Scan(rootDirectory + "/recipe_item.txt", "recipe_item");
		Operator rightOp = getAllProductData();

		leftOp = new Join(leftOp, "recipe_item.product", rightOp, "product.product_id");
		leftOp = new Project(leftOp, new TupleProjector() {

			@Override
			public Tuple project(Tuple t, TupleSchema schema) {
				List<String> newColumns = new ArrayList<String>(t.getSchema().getColumnNames());
				newColumns.add("recipe_item.price");
				List<String> newVals = new ArrayList<String>(t.getValues());
				BigDecimal linePrice = t.getBigDecimal("product.price").multiply(new BigDecimal(t.getInt("recipe_item.amount")));
				newVals.add(linePrice.toString());
				return new Tuple(new TupleSchema(newColumns), newVals);
			}
		});
		rightOp = new GroupBy(leftOp, AggregateFunction.SUM, "recipe_item.price", "recipe.cost_of_ingredients", "recipe_item.recipe");

		leftOp = new Scan(rootDirectory + "/recipe.txt", "recipe");

		return new Join(leftOp, "recipe.recipe_id", rightOp, "recipe_item.recipe");

	}

	@Override
	public Recipe getRecipeById(final int id) {
		Operator op = getAllRecipes();
		op = new Select(op, new TupleAccepter() {
			@Override
			public boolean acceptTuple(Tuple t) {
				return t.getInt("recipe.recipe_id") == id;
			}
		});

		try {
			return op.moveNext() ? new Recipe(op.current()) : null;
		} catch (ParseException e) {
			LOGGER.error("could not create Recpie", e);
			return null;
		}
	}

	@Override
	public List<RecipeItem> getRecipeItemsByRecipe(final Recipe recipe) {
		Operator leftOp = new Scan(rootDirectory + "/recipe_item.txt", "recipe_item");
		Operator rightOp = getAllProductData();

		leftOp = new Join(leftOp, "recipe_item.product", rightOp, "product.product_id");
		leftOp = new Select(leftOp, new TupleAccepter() {

			@Override
			public boolean acceptTuple(Tuple t) {
				return t.getInt("recipe_item.recipe") == recipe.getRecipeId();
			}
		});

		List<RecipeItem> retVal = new ArrayList<RecipeItem>();
		leftOp = new Project(leftOp, new TupleProjector() {

			@Override
			public Tuple project(Tuple t, TupleSchema schema) {
				List<String> newColumns = new ArrayList<String>(t.getSchema().getColumnNames());
				newColumns.add("recipe_item.price");
				List<String> newVals = new ArrayList<String>(t.getValues());
				BigDecimal linePrice = t.getBigDecimal("product.price").multiply(new BigDecimal(t.getInt("recipe_item.amount")));
				newVals.add(linePrice.toString());
				return new Tuple(new TupleSchema(newColumns), newVals);
			}
		});
		while (leftOp.moveNext()) {
			retVal.add(new RecipeItem(leftOp.current(), recipe));
		}
		return retVal;

	}

	@Override
	public List<ShoppingCartLine> getShoppingCartLinesForUser(final RegisteredUser user) {
		Operator op = getAllShoppingCartLines();

		op = new Select(op, new TupleAccepter() {
			@Override
			public boolean acceptTuple(Tuple t) {
				return t.getInt("shopping_cart.user") == user.getUserId();
			}
		});

		List<ShoppingCartLine> retVal = new ArrayList<ShoppingCartLine>();
		while (op.moveNext()) {
			retVal.add(new ShoppingCartLine(op.current()));
		}
		return retVal;

	}

	private Operator getAllShoppingCartLines() {
		Operator leftOp = new Scan(rootDirectory + "/shopping_cart_line.txt", "shopping_cart_line");
		Operator rightOp = getAllProductData();

		rightOp = new Join(leftOp, "shopping_cart_line.product", rightOp, "product.product_id");
		leftOp = new Scan(rootDirectory + "/shopping_cart.txt", "shopping_cart");

		leftOp = new Join(leftOp, "shopping_cart.shopping_cart_id", rightOp, "shopping_cart_line.shopping_cart");

		// add a new line to each tuple, that consists of
		// shopping_cart_line.amount * product.price
		return new Project(leftOp, new TupleProjector() {
			@Override
			public Tuple project(Tuple t, TupleSchema schema) {
				List<String> newColumns = new ArrayList<String>(t.getSchema().getColumnNames());
				newColumns.add("shopping_cart_line.line_price");
				List<String> newVals = new ArrayList<String>(t.getValues());
				BigDecimal linePrice = t.getBigDecimal("product.price").multiply(new BigDecimal(t.getInt("shopping_cart_line.amount")));
				newVals.add(linePrice.toString());
				return new Tuple(new TupleSchema(newColumns), newVals);
			}
		});
	}

	@Override
	public BigDecimal getShoppingCartPriceForUser(final RegisteredUser user) {
		Operator op = getAllShoppingCartLines();
		// first select all for given user
		op = new Select(op, new TupleAccepter() {
			@Override
			public boolean acceptTuple(Tuple t) {
				return t.getInt("shopping_cart.user") == user.getUserId();
			}
		});

		// sum up the whole thing
		op = new GroupBy(op, AggregateFunction.SUM, "shopping_cart_line.line_price", "total_price", "shopping_cart.user");

		return op.moveNext() ? op.current().getBigDecimal("total_price") : new BigDecimal(-1);

	}

	@Override
	public void saveComment(Comment c) {
		// get max comment_id
		Operator op = new Scan(rootDirectory + "/comment.txt", "comment");

		op = new Sort(op, new Comparator<Tuple>() {
			@Override
			public int compare(Tuple o1, Tuple o2) {
				return ((Integer) o2.getInt("comment.comment_id")).compareTo(o1.getInt("comment.comment_id"));
			}
		});

		op.moveNext();
		int maxCommentId = op.current().getInt("comment.comment_id");

		List<String> commentValues = Arrays.asList((maxCommentId + 1) + "", c.getProduct().getProductId() + "", c.getUser().getUserId() + "", c.getText(),
				Tuple.DATE_FORMAT.format(Calendar.getInstance().getTime()));
		// comment_id;product;user;text;date

		Insert insert = new Insert(rootDirectory + "/comment.txt");
		List<Tuple> tuples = Arrays.asList(new Tuple(new TupleSchema(Arrays.asList("comment_id", "product", "user", "text", "date")), commentValues));

		try {
			insert.makeInsert(tuples);
		} catch (IOException e) {
			LOGGER.error("could not insert new comment", e);
		}
	}

	@Override
	public void addLike(final RegisteredUser currentlyLoggedInUser, final Integer productID) {
		Operator op = new Scan(rootDirectory + "/like.txt", "like");

		// check whether an entry for the given product and user already exists,
		// because of the constraint, that a user can like a product only once;
		op = new Select(op, new TupleAccepter() {
			@Override
			public boolean acceptTuple(Tuple t) {
				return t.getInt("like.user_id") == currentlyLoggedInUser.getUserId() && t.getInt("like.product_id") == productID;
			}
		});
		// this means such an entry does not yet exist
		if (!op.moveNext()) {

			List<String> colNames = Arrays.asList("user_id", "product_id");
			List<String> values = Arrays.asList(currentlyLoggedInUser.getUserId() + "", productID + "");
			List<Tuple> tuples = Arrays.asList(new Tuple(new TupleSchema(colNames), values));

			Insert insert = new Insert(rootDirectory + "/like.txt");

			try {
				insert.makeInsert(tuples);
			} catch (IOException e) {
				LOGGER.error("could not create new Like-Entry", e);
			}
		}

	}

	@Override
	public void addToShoppingCart(final RegisteredUser currentlyLoggedInUser, Integer productId) {

		// not doing it with operators, its easier this way....
		List<ShoppingCartLine> shoppingCartOfUser = getShoppingCartLinesForUser(currentlyLoggedInUser);

		// fetching users shoppingCartId
		Operator op = new Scan(rootDirectory + "/shopping_cart.txt", "shopping_cart");
		op = new Select(op, new TupleAccepter() {
			@Override
			public boolean acceptTuple(Tuple t) {
				return t.getInt("shopping_cart.user") == currentlyLoggedInUser.getUserId();
			}
		});
		op.moveNext();
		int usersShoppingCartId = op.current().getInt("shopping_cart.shopping_cart_id");

		// determining whether we have to do an insert or an update (if product
		// already in shoppingCart add amount+1) make an update,
		// otherwise make an insert
		int shoppingCartLineId = -1;
		for (ShoppingCartLine shoppingCartLine : shoppingCartOfUser) {
			if (shoppingCartLine.getProductId() == productId)
				shoppingCartLineId = shoppingCartLine.getCartLineId();
		}
		// make the insert
		if (shoppingCartLineId == -1) {
			op = new Scan(rootDirectory + "/shopping_cart_line.txt", "shopping_cart_line");
			op = new Sort(op, new Comparator<Tuple>() {
				@Override
				public int compare(Tuple o1, Tuple o2) {
					return ((Integer) o2.getInt("shopping_cart_line.shopping_cart_line_id")).compareTo(o1.getInt("shopping_cart_line.shopping_cart_line_id"));
				}
			});

			op.moveNext();
			int maxShoppingCartId = op.current().getInt("shopping_cart_line.shopping_cart_line_id") + 1;

			// shopping_cart_line_id;product;amount;shopping_cart
			List<String> colNames = Arrays.asList("shopping_cart_line_id", "product", "amount", "shopping_cart");
			List<String> values = Arrays.asList(maxShoppingCartId + "", productId + "", "1", usersShoppingCartId + "");
			List<Tuple> tuples = Arrays.asList(new Tuple(new TupleSchema(colNames), values));
			Insert insert = new Insert(rootDirectory + "/shopping_cart_line.txt");

			try {
				insert.makeInsert(tuples);
			} catch (IOException e) {
				LOGGER.error("could not create new Like-Entry", e);
			}
		} else {
			addOneToShoppingCartLine(shoppingCartLineId);
		}

	}

	@Override
	public void addOneToShoppingCartLine(int cartLineId) {
		// radical method:
		// 1. read all shoppingCartLines into memory
		// 2. update cartLine amount
		// 3. delete the file shopping_cart_line.txt
		// 4. write the file new with updated tuples

		String cartLineFile = rootDirectory + "/shopping_cart_line.txt";
		Operator op = new Scan(cartLineFile, "");
		List<Tuple> allLines = new ArrayList<Tuple>();
		while (op.moveNext()) {
			allLines.add(op.current());
			if (op.current().getInt("shopping_cart_line_id") == cartLineId) {
				Tuple lastAdded = allLines.get(allLines.size() - 1);
				lastAdded.setInt("amount", lastAdded.getInt("amount") + 1);
			}
		}
		File f = new File(cartLineFile);
		if (f.exists()) {
			if (!f.delete()) {
				LOGGER.error("Could not delete file: " + cartLineFile);
			}
		}
		try {
			f.createNewFile();
			TupleSchema schema = allLines.get(0).getSchema();
			FileWriter fstream = new FileWriter(f, true);
			BufferedWriter out = new BufferedWriter(fstream);

			for (int i = 0; i < schema.getColumnNames().size(); i++) {
				out.write(schema.getColumnNames().get(i));
				if (i != (schema.getColumnNames().size() - 1)) {
					out.write(";");
				}
			}
			out.write(System.getProperty("line.separator"));
			out.flush();
			// Close the output stream
			out.close();
		} catch (IOException e) {
			LOGGER.error("Could not create new file:" + cartLineFile);
		}

		Insert insert = new Insert(cartLineFile);
		try {
			insert.makeInsert(allLines);
		} catch (IOException e) {
			LOGGER.error("Could not insert all Tuples", e);
		}

	}

	@Override
	public void removeOneFromShoppingCartLine(int cartLineId) {
		// radical method:
		// 1. read all shoppingCartLines into memory
		// 2. update cartLine amount or remove the Tuple
		// 3. delete the file shopping_cart_line.txt
		// 4. write the file new with updated tuples

		String cartLineFile = rootDirectory + "/shopping_cart_line.txt";
		Operator op = new Scan(cartLineFile, "");
		List<Tuple> allLines = new ArrayList<Tuple>();
		while (op.moveNext()) {
			if (op.current().getInt("shopping_cart_line_id") == cartLineId) {
				int newAmount = op.current().getInt("amount") - 1;
				if (newAmount == 0) {
					continue;
				}
				op.current().setInt("amount", newAmount);
			}
			allLines.add(op.current());
		}
		File f = new File(cartLineFile);
		if (f.exists()) {
			if (!f.delete()) {
				LOGGER.error("Could not delete file: " + cartLineFile);
			}
		}
		try {
			f.createNewFile();
			TupleSchema schema = allLines.get(0).getSchema();
			FileWriter fstream = new FileWriter(f, true);
			BufferedWriter out = new BufferedWriter(fstream);

			for (int i = 0; i < schema.getColumnNames().size(); i++) {
				out.write(schema.getColumnNames().get(i));
				if (i != (schema.getColumnNames().size() - 1)) {
					out.write(";");
				}
			}
			out.write(System.getProperty("line.separator"));
			out.flush();
			// Close the output stream
			out.close();
		} catch (IOException e) {
			LOGGER.error("Could not create new file:" + cartLineFile);
		}

		Insert insert = new Insert(cartLineFile);
		try {
			insert.makeInsert(allLines);
		} catch (IOException e) {
			LOGGER.error("Could not insert all Tuples", e);
		}

	}

	@Override
	public RegisteredUser createUser(final String username, String email, final String password) {

		Operator op = new Scan(rootDirectory + "/registered_user.txt", "registered_user");
		op = new Sort(op, new Comparator<Tuple>() {
			@Override
			public int compare(Tuple o1, Tuple o2) {
				return ((Integer) o2.getInt("registered_user.user_id")).compareTo(o1.getInt("registered_user.user_id"));
			}
		});

		op.moveNext();
		int maxUserId = op.current().getInt("registered_user.user_id") + 1;

		op = new Scan(rootDirectory + "/shopping_cart.txt", "shopping_cart");
		op = new Sort(op, new Comparator<Tuple>() {
			@Override
			public int compare(Tuple o1, Tuple o2) {
				return ((Integer) o2.getInt("shopping_cart.shopping_cart_id")).compareTo(o1.getInt("shopping_cart.shopping_cart_id"));
			}
		});

		op.moveNext();
		int maxShoppingCartId = op.current().getInt("shopping_cart.shopping_cart_id") + 1;

		op = new Scan(rootDirectory + "/registered_user.txt", "user");
		op = new Select(op, new TupleAccepter() {
			@Override
			public boolean acceptTuple(Tuple t) {
				return t.getString("user.name").equals(username) || t.getString("user.email").equals(password);
			}
		});

		// means user not in Database
		if (!op.moveNext()) {
			// user_id;name;passwd;email
			List<String> colNames = Arrays.asList("user_id", "name", "passwd", "email");
			List<String> values = Arrays.asList(maxUserId + "", username, password, email);
			List<Tuple> tuples = Arrays.asList(new Tuple(new TupleSchema(colNames), values));

			colNames = Arrays.asList("shopping_cart_id", "user");
			values = Arrays.asList(maxShoppingCartId + "", maxUserId + "");

			try {
				Insert insert = new Insert(rootDirectory + "/registered_user.txt");
				insert.makeInsert(tuples);

				insert = new Insert(rootDirectory + "/shopping_cart.txt");
				tuples = Arrays.asList(new Tuple(new TupleSchema(colNames), values));
				insert.makeInsert(tuples);
			} catch (IOException e) {
				LOGGER.error("could not create new User or shoppingCart entry", e);
			}

			return new RegisteredUser(username, password, email, maxUserId);
		}
		return null;
	}

}
